package main;

import javafx.animation.*;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
import javafx.util.Duration;
import main.entity.Raft;
import main.utils.GridDisplay;

import static javafx.util.Duration.millis;
import static main.utils.RiverConstants.APPLICATION_TITLE;
import static main.utils.RiverConstants.CELL_SIZE;
import static main.utils.RiverConstants.FRAME_SIZE;
import static main.utils.RiverConstants.GRASS_HEIGHT;
import static main.utils.RiverConstants.RAFT_SHIFTING;
import static main.utils.RiverConstants.TICKS_PER_SECOND;
import static main.utils.RiverFactory.*;
import static main.utils.RiverUtils.*;

public class RiverApp extends Application {
    private Group popup;
    private Raft raft;
    private GridDisplay trap = new GridDisplay();

    private Timeline globalTimeline;
    private Animation raftAnimation;
    private PathTransition barrierAnimation;

    private final Image stoneImage = newImage("img/stone.png", CELL_SIZE, CELL_SIZE);

    private boolean isBarrierAnimationInProgress = false;

    private KeyCode pressedButton = null;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        stage.setTitle(APPLICATION_TITLE);
        Group root = new Group();
        Scene scene = new Scene(root, FRAME_SIZE, FRAME_SIZE);

        raft = createRaft();

        Group content = createContent();
        content.getChildren().add(raft);
        root.getChildren().add(content);

        popup = createPopup();
        root.getChildren().add(popup);

        trap.getDisplay().setLayoutY(GRASS_HEIGHT);
        root.getChildren().add(trap.getDisplay());

        initEvents(stage);
        initAnimation();

        stage.setScene(scene);
        stage.show();
        globalTimeline.play();
    }

    private void initAnimation() {
        final Path path = new Path();

        globalTimeline = new Timeline(new KeyFrame(millis(50), event -> {
            if (!isPositionSafe(trap, raft)) {
                popup.setVisible(true);
                globalTimeline.stop();
                barrierAnimation.stop();
                isBarrierAnimationInProgress = false;
                return;
            }
            if (!isBarrierAnimationInProgress) {
                forceNewTrap(stoneImage, trap);

                double trapHeight = trap.getHeight();
                double trapWidth = trap.getWidth();
                double y = (trap.getY() + trapHeight) / 2;
                path.getElements().clear();
                path.getElements().add(new MoveTo(FRAME_SIZE + trapWidth / 2, y));
                path.getElements().add(new LineTo(-trapWidth / 2, y));
                isBarrierAnimationInProgress = true;
                barrierAnimation.play();
                trap.getDisplay().setVisible(true);
            }
        }));
        globalTimeline.setCycleCount(Animation.INDEFINITE);

        barrierAnimation = new PathTransition();
        barrierAnimation.setDuration(Duration.millis(8000));
        barrierAnimation.setPath(path);
        barrierAnimation.setNode(trap.getDisplay());
        barrierAnimation.setCycleCount(1);
        barrierAnimation.setAutoReverse(false);
        barrierAnimation.setOnFinished(event -> {
            barrierAnimation.stop();
            isBarrierAnimationInProgress = false;
        });

        raftAnimation = new Transition(TICKS_PER_SECOND) {
            {
                setCycleDuration(Duration.INDEFINITE);
            }

            protected void interpolate(double frac) {
                if (pressedButton != null && (pressedButton == KeyCode.UP || pressedButton == KeyCode.W)) {
                    int newValue = raft.getY() - RAFT_SHIFTING;
                    if (newValue < GRASS_HEIGHT) {
                        return;
                    }
                    raft.setY(newValue);
                    raft.updatePositions();
                    movePaddle(raft, true);
                } else if (pressedButton != null && (pressedButton == KeyCode.DOWN || pressedButton == KeyCode.S)) {
                    int newValue = raft.getY() + RAFT_SHIFTING;
                    if (newValue > FRAME_SIZE - GRASS_HEIGHT - 30) {
                        return;
                    }
                    raft.setY(newValue);
                    raft.updatePositions();
                    movePaddle(raft, false);
                } else if (pressedButton == KeyCode.SPACE) {
                    popup.setVisible(false);
                    globalTimeline.play();
                }
            }
        };
    }

    private void initEvents(Stage stage) {
        stage.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                pressedButton = event.getCode();
                raftAnimation.play();
            }
        });
        stage.addEventHandler(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                if (event.getCode() == pressedButton) {
                    pressedButton = null;
                    raftAnimation.stop();
                }
            }
        });
    }
}
